use leptos::*;
use leptos_meta::*;
use log::info;
use log::error;

const MANY_COUNTERS: usize = 1000;

type CounterHolder = Vec<(usize, (ReadSignal<i32>, WriteSignal<i32>))>;

#[derive(Copy, Clone)]
struct CounterUpdater {
    set_counters: WriteSignal<CounterHolder>,
}

#[component]
pub fn Counters() -> impl IntoView {
    let (next_counter_id, set_next_counter_id) = create_signal(0);
    let (counters, set_counters) = create_signal::<CounterHolder>(vec![]);
    provide_context(CounterUpdater { set_counters });

    let add_counter = move |_| {
        let id = next_counter_id.get();
        let sig = create_signal(0);
        set_counters.update(move |counters| counters.push((id, sig)));
        set_next_counter_id.update(|id| *id += 1);
    };

    let add_many_counters = move |_| {
        let next_id = next_counter_id.get();
        let new_counters = (next_id..next_id + MANY_COUNTERS).map(|id| {
            let signal = create_signal(0);
            (id, signal)
        });
        set_counters.update(move |counters| counters.extend(new_counters));
        set_next_counter_id.update(|id| *id += MANY_COUNTERS);
    };

    let clear_counters = move |_| {
        set_counters.update(|counters| counters.clear());
    };

    ///////////////////// x counters input ///////////////////////////////
    let (x_counters_input, set_x_counters_input) = create_signal(2);
    let add_x_counters = move |_| {
        let v = x_counters_input.get() as usize;

        info!("add_x_counters: {}", v);
        let next_id = next_counter_id.get();
        let new_counters = (next_id..next_id + v).map(|id| {
            let signal = create_signal(0);
            (id, signal)
        });
        set_counters.update(move |counters| counters.extend(new_counters));
        set_next_counter_id.update(|id| *id += v);
    };

    view! {
        <Title text="Counters (Stable)" />
        <div style="background:#e5ffe5">
            <button on:click=add_counter>
                "Add Counter"
            </button>
            <button on:click=add_many_counters>
                {format!("Add {MANY_COUNTERS} Counters")}
            </button>
            <button on:click=clear_counters>
                "Clear Counters"
            </button>
            <p>
            <input data-testid="add_x_counters_input" type="text" on:input=move |ev| {
                let x = event_target_value(&ev).parse::<i32>().unwrap_or_default();
                set_x_counters_input.set(x)
              }
              prop:value=x_counters_input/>
            <button on:click=add_x_counters>
                "Add " {move || x_counters_input.get()} " counters"
            </button>
            </p>

            <p>
                "Total: "
                <span data-testid="total">{move ||
                    counters.get()
                        .iter()
                        .map(|(_, (count, _))| count.get())
                        .sum::<i32>()
                        .to_string()
                }</span>
                " from "
                <span data-testid="counters">{move || counters.with(|counters| counters.len()).to_string()}</span>
                " counters."
            </p>
            <ul>
                <For
                    each={move || counters.get()}
                    key={|counter| counter.0}
                    children=move |(id, (value, set_value))| {
                        view! {
                            <Counter id value set_value/>
                        }
                    }
                />
            </ul>
        </div>
    }
}

#[component]
pub fn Selector() -> impl IntoView {
    ////////////////////// SelectOption //////////////////////////////
    #[derive(Debug, Clone, Copy)]
    pub struct SelectOption {
        pub label: &'static str,
        pub pos: usize,
    }
    let options: Vec<SelectOption> =  vec![
        SelectOption{ label: "h0rses", pos: 0},
        SelectOption{ label: "b1rds", pos: 1},
        SelectOption{ label: "2nfish", pos: 2},
    ];

    let optionsClone = options.clone();
    let selection: RwSignal<Option<usize>> = create_rw_signal(Some(1)); // initial selection

    view! {
      <div style="background:#ffffbf">
      <select
        id = "myselect"
        on:change = move |ev| {
          let new_selection = event_target_value(&ev);
          if new_selection.is_empty() {
            selection.set(None);
          } else {
            match new_selection.parse() {
              Ok(v) => {
                info!("you selected {}", v);
                selection.set(Some(v))
              },
              Err(_) => {
                error!("Error: Unexpected option value {new_selection}");
              },
            }
          }
        }
      >
      <For
        each = move || optionsClone.clone()
        key = |option| option.pos
        let:option
          >
          <option
            value = option.pos
            selected = (selection.get() == Some(option.pos))
            >
            { option.label }
          </option>
      </For>
      </select>
        <p>
        "You selected: "
        <span data-testid="myselection">{move || {
          match selection.get() {
            Some(v) => {
              options[v].label
            },
            None => "no idea..."
          }
        }
        }</span>
        </p>
      </div>
    }
}

#[component]
fn Counter(
    id: usize,
    value: ReadSignal<i32>,
    set_value: WriteSignal<i32>,
) -> impl IntoView {
    let CounterUpdater { set_counters } = use_context().unwrap();
    let input = move |ev| {
        set_value
            .set(event_target_value(&ev).parse::<i32>().unwrap_or_default())
    };

    // this will run when the scope is disposed, i.e., when this row is deleted
    // because the signal was created in the parent scope, it won't be disposed
    // of until the parent scope is. but we no longer need it, so we'll dispose of
    // it when this row is deleted, instead. if we don't dispose of it here,
    // this memory will "leak," i.e., the signal will continue to exist until the
    // parent component is removed. in the case of this component, where it's the
    // root, that's the lifetime of the program.
    on_cleanup(move || {
        log::debug!("deleted a row");
        value.dispose();
    });

    view! {
        <li>
            <button data-testid="decrement_count" on:click=move |_| set_value.update(move |value| *value -= 1)>"-1"</button>
            <input data-testid="counter_input" type="text"
                prop:value={move || value.get().to_string()}
                on:input=input
            />
            <span>{value}</span>
            <button data-testid="increment_count" on:click=move |_| set_value.update(move |value| *value += 1)>"+1"</button>
            <button data-testid="remove_counter" on:click=move |_| set_counters.update(move |counters| counters.retain(|(counter_id, _)| counter_id != &id))>"x"</button>
        </li>
    }
}

#[component]
pub fn DynSelector() -> impl IntoView {
    // type SelectionHolder = Vec<(usize, (ReadSignal<i32>, WriteSignal<i32>))>;

    // #[derive(Copy, Clone)]
    // struct SelectionUpdater {
    //     set_selectionOptions: WriteSignal<SelectionHolder>,
    // }
    #[derive(Debug, Clone, Copy)]
    pub struct SelectionOption {
        pub label: &'static str,
        pub id: u32,
        pub amount: RwSignal<u32>,
    }
    let selectionOptions1: Vec<SelectionOption> = vec![
        SelectionOption{ label: "h0rses", id: 0, amount: create_rw_signal::<u32>(0)},
        SelectionOption{ label: "b1rds", id: 1, amount: create_rw_signal::<u32>(10)},
        SelectionOption{ label: "2nfish", id: 2, amount: create_rw_signal::<u32>(20)},
        SelectionOption{ label: "3lk", id: 3, amount: create_rw_signal::<u32>(30)},
    ];
    let default_selection: RwSignal<Option<u32>> = create_rw_signal(Some(0));
    let selectionOptions = create_rw_signal::<Vec<SelectionOption>>(selectionOptions1);
    let print = move |_| {
        let s = selectionOptions.get();
        info!("{} {}", s[0].label, s[0].amount.get());
    };
    let add_option = move |_| {
        let a = SelectionOption{ label: "4eel", id: 4, amount: create_rw_signal::<u32>(20)};
        selectionOptions.update(|n| {
            if n.len() > 0 {
                n[0].amount.set(200);
                info!("n[0]: {} {}", n[0].label, n[0].amount.get())
            }
            n.push(a);
        });
    };
    view! {
      <div style="background:#eae3ff">
        <select
          id = "mymultiselect"
          on:change = move |ev| {
            let target_value = event_target_value(&ev);
            if target_value.is_empty() {
              default_selection.set(None);
            } else {
               match target_value.parse() {
                 Ok(v) => {
                   info!("you selected {}", v);
                   default_selection.set(Some(v))
                 },
                 Err(_) => {
                   error!("Error: Unexpected option value {target_value}");
                 },
              }
            }
          }
        >
          <For
            each = {move || selectionOptions.clone().get()}
            key = |option| option.id
            let:option
          >
            <option
              value = move || option.id
              default_selection = (default_selection.get() == Some(option.id))
            > { move || {
               let z = option.amount.get();
               let v = format!("{} - {}", option.label, z);
               v
               }
              }
            </option>
          </For>
        </select>
        <p>
        "You selected: "
        <span data-testid="mymultiselection">{move || {
          let selected_option = default_selection.get();
            match selected_option {
                Some(v) => {
                    let l = selectionOptions.get()[v as usize].label;
                    let a = selectionOptions.get()[v as usize].amount.get();
                    format!("{} - {}", l, a.to_string())
                },
                None => "no idea...".to_string()
            }
        }
        }</span>
         <button on:click=add_option>
           "Add another option to selector"
         </button>
         <button on:click=print>
           "Print"
         </button>
        </p>
      </div>
    }
}
